/*      Copyright 2010 Jesus McCloud <jesus@orion>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

//compile with --pkg gtksourceview-2.0 --pkg libxml-2.0 --pkg gtk+-2.0  --pkg gmodule-2.0 --pkg gee-1.0

using Gtk;

namespace jmc{
public class GladeV : GLib.Object{
  const string GLADE = "GladeV.glade";
  
  const string WIDGET_DECLARATION = "/*Widget Declaration*/";
  const string CONSTRUCTOR = "/*Default Constructor*/";
  const string WIDGET_CREATION = "/*Widget Creation*/";
  const string CREATE_OBJECT_AND_RUN = "/*Create new Object of Class*/";
  const string CONNECT_DESTROY = "/*Connect Destroy*/";
  const string SIGNAL_HANDLER = "/*Signal Handlers*/";
  
  //variable declaration  
  bool file_opened;
  Gee.LinkedList<string> widgets;
  Gee.HashSet<string> handlers;
  Gee.LinkedList<string> code_template;
  string template_file= "Template.vala";
  int index;
  string class_name;
  string variable_name;
  string klass;
  string file_name;
  StringBuilder text;
  Gtk.SourceBuffer syntax_highlight;
  Gtk.SourceLanguageManager lang_man;
  Gtk.TreeIter widget_table_iter;
  Gtk.ListStore widget_table;
  Gtk.CellRendererToggle renderer_toggle;
  Gtk.CellRendererText renderer_text;
  //Widget Declaration
  private Gtk.TreeView treeview1;
  private Gtk.Button bt_all;
  private Gtk.Button bt_with_handlers;
  private Gtk.Button bt_none;
  private Gtk.TreeViewColumn column_widget;
  private Gtk.TreeViewColumn column_toggle;  
  public Gtk.Window main_window {get; set;}
  private Gtk.VBox vbox1;
  private Gtk.MenuBar menubar1;
  private Gtk.MenuItem menuitem1;
  private Gtk.Menu menu1;
  private Gtk.ImageMenuItem mi_open;
  private Gtk.ImageMenuItem mi_save;
  private Gtk.ImageMenuItem mi_quit;
  private Gtk.MenuItem menuitem4;
  private Gtk.Menu menu3;
  private Gtk.ImageMenuItem mi_about;
  private Gtk.HPaned hpaned1;
  private Gtk.ScrolledWindow scrolledwindow1;
  private Gtk.SourceView tv_source;
  private Gtk.VBox vbox2;
  private Gtk.HBox hbox2;
  private Gtk.Label label1;
  private Gtk.Entry entry_namespace;
  private Gtk.HBox hbox3;
  private Gtk.Label label2;
  private Gtk.Entry entry_class;
  private Gtk.HBox hbox4;
  private Gtk.Label label3;
  private Gtk.HBox hbox5;
  private Gtk.Button bt_generate;
  private Gtk.Statusbar statusbar1;
  private Gtk.Label lb_status;
  private Gtk.Image image1;
  private Gtk.AboutDialog aboutdialog1;
  private Gtk.VBox dialog_vbox1;
  private Gtk.ComboBox cb_main_window;
  
  
  public GladeV(){  
    //varaible initailization
    file_opened=false;
    syntax_highlight = new Gtk.SourceBuffer(null);
    code_template= new Gee.LinkedList<string>(null);
    text = new StringBuilder();
    widgets = new Gee.LinkedList<string>(null);
    handlers = new Gee.HashSet<string>();
    index=0;
    klass ="Klass";
    class_name="class_name";
    variable_name= "varaible_name";
    file_name="file_name";
    lang_man = Gtk.SourceLanguageManager.get_default();
    init_template(template_file);
    create_ui();
    
    ///////
       
    /////////
    
  }

  private void init_template(string template_file_name){
    try{
      string file_tmp;
      FileUtils.get_contents(template_file_name,out file_tmp,null);
      string[] template_lines= file_tmp.split("\n");
      code_template.clear();
      for(int i=0;i<template_lines.length;i++){
        code_template.add(template_lines[i]);
      }
    }catch(GLib.Error e){
      stderr.printf("Template not present");
    }
  }


  private void create_ui(){
    try
    {
      Gtk.Builder builder  =  new Gtk.Builder ();
      builder.add_from_file (GLADE);
         
      //Widget Creation
      
       

      
      main_window = builder.get_object("main_window") as Gtk.Window;

      vbox1 = builder.get_object("vbox1") as Gtk.VBox;

      menubar1 = builder.get_object("menubar1") as Gtk.MenuBar;

      menuitem1 = builder.get_object("menuitem1") as Gtk.MenuItem;

      menu1 = builder.get_object("menu1") as Gtk.Menu;

      mi_open = builder.get_object("mi_open") as Gtk.ImageMenuItem;
      mi_open.activate+=(on_mi_open_activate_item);

      mi_save = builder.get_object("mi_save") as Gtk.ImageMenuItem;
      mi_save.activate+=(on_mi_save_activate_item);

      mi_quit = builder.get_object("mi_quit") as Gtk.ImageMenuItem;
      mi_quit.activate+=(on_mi_quit_activate_item);

      menuitem4 = builder.get_object("menuitem4") as Gtk.MenuItem;

      menu3 = builder.get_object("menu3") as Gtk.Menu;

      mi_about = builder.get_object("mi_about") as Gtk.ImageMenuItem;
      mi_about.activate+=(on_mi_about_activate);

      hpaned1 = builder.get_object("hpaned1") as Gtk.HPaned;

      scrolledwindow1 = builder.get_object("scrolledwindow1") as Gtk.ScrolledWindow;

      tv_source = builder.get_object("tv_source") as Gtk.SourceView;

      vbox2 = builder.get_object("vbox2") as Gtk.VBox;

      hbox2 = builder.get_object("hbox2") as Gtk.HBox;

      label1 = builder.get_object("label1") as Gtk.Label;

      entry_namespace = builder.get_object("entry_namespace") as Gtk.Entry;

      hbox3 = builder.get_object("hbox3") as Gtk.HBox;

      label2 = builder.get_object("label2") as Gtk.Label;

      entry_class = builder.get_object("entry_class") as Gtk.Entry;

      hbox4 = builder.get_object("hbox4") as Gtk.HBox;

      label3 = builder.get_object("label3") as Gtk.Label;

      hbox5 = builder.get_object("hbox5") as Gtk.HBox;

      bt_generate = builder.get_object("bt_generate") as Gtk.Button;
      bt_generate.clicked+=(on_bt_generate_clicked);

      bt_all = builder.get_object("bt_all") as Gtk.Button;
      bt_all.clicked+=(on_bt_all_clicked);

      bt_with_handlers = builder.get_object("bt_with_handlers") as Gtk.Button;
      bt_with_handlers.clicked+=(on_bt_with_handlers_clicked);

      bt_none = builder.get_object("bt_none") as Gtk.Button;
      bt_none.clicked+=(on_bt_none_clicked);

      statusbar1 = builder.get_object("statusbar1") as Gtk.Statusbar;

      lb_status = builder.get_object("lb_status") as Gtk.Label;

      image1 = builder.get_object("image1") as Gtk.Image;

      aboutdialog1 = builder.get_object("aboutdialog1") as Gtk.AboutDialog;

      dialog_vbox1 = builder.get_object("dialog_vbox1") as Gtk.VBox;      
      
      treeview1 = builder.get_object("treeview1") as Gtk.TreeView;

      column_toggle = builder.get_object("toggle_column") as Gtk.TreeViewColumn;
      column_widget = builder.get_object("widget_column") as Gtk.TreeViewColumn;
      
      
      cb_main_window= new Gtk.ComboBox.text();
      cb_main_window.append_text("");
      cb_main_window.set_active(0);
      hbox4.add(cb_main_window);
      
      widget_table = builder.get_object("widget_table") as ListStore;//new ListStore(2, typeof (bool), typeof (string));
      //treeview1.model=widget_table;
      renderer_toggle = new CellRendererToggle ();
      column_toggle.pack_start(renderer_toggle, false);
      column_toggle.add_attribute (renderer_toggle, "active", 0);
      renderer_text = new CellRendererText ();
      column_widget.pack_start (renderer_text, true);
      column_widget.add_attribute (renderer_text, "text", 1);
        
      renderer_toggle.toggled.connect(on_table_toggled);
      
      
      
      main_window.destroy.connect(Gtk.main_quit);

    }catch (GLib.Error e) {
        stderr.printf ("Could not load UI: %s\n", e.message);
    }
  }
  
  private void on_table_toggled(CellRendererToggle toggle, string path){
    var tree_path = new TreePath.from_string (path);
    TreeIter iter;
    widget_table.get_iter (out iter, tree_path);
    widget_table.set (iter, 0, !renderer_toggle.active);
    setup_cbox();
  }
  
  
  private void fill_table(){
    widget_table.clear();
    for(int i=0;i<widgets.size;i++){         
        widget_table.append (out widget_table_iter);
        widget_table.set (widget_table_iter, 0, true, 1, ((widgets.get(i)).split(";"))[1]);
    }
  }
  
  private void clear_cbox(){
     for(int i=widgets.size-1;i>=0;i--){
        cb_main_window.remove_text(1);
      }
  }

  private int[] init_widget_indices(){
    int[] temp_ind={};
    int[] result_indices;
    int number_indices=0;
    TreeIter iter;
    bool t=false;
    for(int i=0;i<widgets.size;i++){
      widget_table.get_iter (out iter, new TreePath.from_indices(i));
      widget_table.get(iter,0, out t);
      if(t){
        number_indices++;
        temp_ind+=i;
      }
    }
    result_indices=new int[number_indices];
    for(int i=0;i<number_indices;i++)
      result_indices[i] =temp_ind[i];
    return result_indices;
  }
  
  private void open_file(string file_name){
    if(file_opened)
    {
      index=0;
      clear_cbox();
      widgets.clear();
    }
    Xml.Parser.init();
  
    parse_file(file_name);
    fill_table();
    init_widget_indices();
    bt_generate.sensitive=true;
    try{
      string tmp;
      FileUtils.get_contents(file_name,out tmp,null);
      tv_source.buffer = new Gtk.SourceBuffer.with_language(lang_man.get_language("xml"));
      tv_source.buffer.text=tmp;
      lb_status.label=file_name+" loaded.";
    }catch(GLib.Error e){
      tv_source.buffer.text="ERROR";
      lb_status.label="Error loading File "+file_name+"!";
    }
      
    for(int i=0;i<widgets.size;i++){
      cb_main_window.append_text(widgets[i].split(";")[1]);
    }
    Xml.Parser.cleanup();
    file_opened=true;
  }
  
  private void parse_file (string path) {
    Xml.Doc* doc = Xml.Parser.parse_file (path);
    if (doc == null) {
      stderr.printf ("File %s not found or permissions missing", path);
      return;
    }

    // Get the root node.
    Xml.Node* root = doc->get_root_element ();
    if (root == null) {
      // Free the document manually before returning
      delete doc;
      stderr.printf ("The xml file '%s' is empty", path);
      return;
    }
    
    parse_node (root);
    delete doc;
  }
  
  private void parse_node (Xml.Node* node) {
    // Loop over the passed node's children
    for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
      // Spaces between tags are also nodes, discard them
      if (iter->type != Xml.ElementType.ELEMENT_NODE) {
        continue;
      }
      // Parse the node's properties (attributes) ...
      parse_properties (iter);
      // Followed by its children nodes
      parse_node (iter);
    }
  }
  
  private void parse_properties (Xml.Node* node) {
    // Loop over the passed node's properties (attributes)
    for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {
      string attr_name = prop->name;
      int oldindex=index;
      // Notice the ->children which points to a Node*
      // (Attr doesn't feature content)
      string attr_content = prop->children->content;
      if(node->name=="signal"){
        if(oldindex==index)
        {
          class_name+=attr_content+";";
        }
        attr_name=index.to_string()+" signal "+attr_name;
      }else{
        if(attr_name=="class"||attr_name=="id"){
          if(attr_name=="class"){
            index++;
            if(oldindex!=index)
            {
              if(oldindex!=0)
                widgets.add(class_name);
              class_name=attr_content.replace("Gtk","Gtk.")+";";
            }
          }else{
            class_name+=attr_content+";";
          }
        }
      }
    }
  }
  
  private void create_body_head(){
    
   // code template.insert(0,("  const string GLADE = \"%s\";\n".printf(file_name)));
    code_template.insert(0, "  const string GLADE =\"%s\";\n".printf(file_name));
    if(entry_class.text!="") klass= entry_class.text;
    code_template.insert(0,"public class %s : GLib.Object{".printf(klass));
    
    
    if(entry_namespace.text!="") code_template.insert(0,"namespace %s{".printf(entry_namespace.text));
  }
  
  
  private void create_body_variables(){
    int wid_i =code_template.index_of(WIDGET_DECLARATION);
    int[] widget_indices= init_widget_indices();
    for(int i=0;i<widget_indices.length;i++){
      string[] variable=(widgets.get(widget_indices[i])).split(";");
      string declare;
        if(cb_main_window.get_active_text()==variable[1]){
          declare ="  public "+variable[0]+" "+variable[1]+" {get; set;}";
        }else
          declare ="  private "+variable[0]+" "+variable[1]+";";
        code_template.insert(wid_i+1,declare);
        
    }
  }
  
  private void create_constructor(){
    int wid_i=code_template.index_of(CONSTRUCTOR);
    code_template.insert(wid_i+1,"  public "+klass+"(){");
  }
  
  private void create_widgets(){
    //Widget Creation
    int wid_i=code_template.index_of(WIDGET_CREATION);
    
    int[] widget_indices= init_widget_indices();
    for(int i=0;i<widget_indices.length;i++){
      string[] variable=(widgets.get(widget_indices[i])).split(";");
      string declare ="      "+variable[1]+" = builder.get_object(\""+variable[1]+"\") as "+variable[0]+";\n";
      //connect handler
      if(variable.length>2){
        for(int h=2;h<variable.length-1;h+=2){
          //mem_toggle.toggled.connect(on_bt_mem_toggled);
          declare += "      "+variable[1]+"."+variable[h]+"+=("+variable[h+1]+");\n";          
        }
        
      }
      code_template.insert(wid_i+1,declare);
    }
  }
  private void create_connect_destroy(){
    if(cb_main_window.get_active()!=0){
      int wid_i=code_template.index_of(CONNECT_DESTROY);
      code_template.insert(wid_i+1,"      %s.destroy.connect(Gtk.main_quit);".printf(cb_main_window.get_active_text()));
    }
  }
  private void create_handlers(){
    
    int[] widget_indices= init_widget_indices();
    for(int i=0;i<widget_indices.length;i++){
      string[] variable=(widgets.get(widget_indices[i])).split(";");
      if(variable.length>2){
        for(int h=2;h<variable.length-1;h+=2){
          string declare ="  [CCode (instance_pos = -1)]\n  private void "+variable[h+1]+"("+variable[0]+" source){\n  \n  }\n";
            
          handlers.add(declare);
          
        }
        
      }
      
    }
    
    Gee.Iterator<string> handler_it =handlers.iterator();
    int wid_i=code_template.index_of(SIGNAL_HANDLER);
    while(handler_it.has_next()){
      handler_it.next();
      code_template.insert(wid_i+1,handler_it.get());
    }
  }
  
  private void create_object_and_run(){
    int wid_i=code_template.index_of(CREATE_OBJECT_AND_RUN);
    code_template.insert(wid_i+1,"    %s %s =  new %s();".printf(klass, klass.down(), klass));
    if(cb_main_window.get_active()!=0)
      code_template.insert(wid_i+2,"    %s.%s.show_all ();".printf(klass.down(),cb_main_window.get_active_text()));
    
    if(entry_namespace.text!="") code_template.add("}\n");
      ///////////////////
  }
  
  
  ////////////
  
  [CCode (instance_pos = -1)]
  private void on_mi_open_activate_item(Gtk.ImageMenuItem imagemenuitem){
    var filechooser = new Gtk.FileChooserDialog("Open File",
    main_window,Gtk.FileChooserAction.OPEN, Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
    Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT, null);
    
    if(filechooser.run()==Gtk.ResponseType.ACCEPT){
      open_file(filechooser.get_filename());        
      string[]filenames= filechooser.get_filename().split("/");
      file_name= filenames[filenames.length-1];
    }
    filechooser.destroy();
  }


  [CCode (instance_pos = -1)]
  private void on_mi_save_activate_item(Gtk.ImageMenuItem imagemenuitem){
    var filechooser = new Gtk.FileChooserDialog("Save File",
      main_window,Gtk.FileChooserAction.SAVE, Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
      Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT, null);
    
    if(filechooser.run()==Gtk.ResponseType.ACCEPT){
      try{
        FileUtils.set_contents(filechooser.get_filename(),tv_source.buffer.text);
        lb_status.label="File saved!";
      }catch(GLib.Error e)
      {
        lb_status.label="Error saving file!";
      }
    }
    filechooser.destroy();
  }


  [CCode (instance_pos = -1)]
  private void on_mi_quit_activate_item(Gtk.ImageMenuItem imagemenuitem){
  Gtk.main_quit();
  }


  [CCode (instance_pos = -1)]
  private void on_mi_about_activate(Gtk.ImageMenuItem imagemenuitem){
  
  }

  
  private void setup_cbox(){
    int[] indices= init_widget_indices();
    clear_cbox();
    string[] temp;
    for(int i=0;i<indices.length;i++){
      temp=(widgets.get(indices[i]).split(";"));
        cb_main_window.append_text(temp[1]);
    }
    
    
  }

  [CCode (instance_pos = -1)]
  private void on_bt_with_handlers_clicked(Gtk.Button source){
  TreeIter iter;
  clear_cbox();
  string[] temp;
    for(int i=0;i<widgets.size;i++){
      widget_table.get_iter (out iter, new TreePath.from_indices(i));
      temp=(widgets.get(i).split(";"));
      if(temp.length==3){
        widget_table.set (iter, 0, false);
      }else{
        widget_table.set (iter, 0, true);
        cb_main_window.append_text(temp[1]);
      }
    }
  }

  [CCode (instance_pos = -1)]
  private void on_bt_none_clicked(Gtk.Button source){
    TreeIter iter;
    for(int i=0;i<widgets.size;i++){
      widget_table.get_iter (out iter, new TreePath.from_indices(i));
      widget_table.set (iter, 0, false);
    }
   
    clear_cbox();
  }

  

  [CCode (instance_pos = -1)]
  private void on_bt_all_clicked(Gtk.Button source){
    TreeIter iter;
    for(int i=0;i<widgets.size;i++){
      widget_table.get_iter (out iter, new TreePath.from_indices(i));
      widget_table.set (iter, 0, true);
    }
    init_widget_indices();
    clear_cbox();
    
    for(int i=0;i<widgets.size;i++){
      cb_main_window.append_text(widgets[i].split(";")[1]);
    }    
  }

  [CCode (instance_pos = -1)]
  private void on_bt_generate_clicked(Gtk.Button button){
    text.erase();
    handlers.clear();
    init_template(template_file);
    create_body_head();
    create_body_variables();
    create_constructor();
    create_widgets();
    create_connect_destroy();
    create_handlers();
    create_object_and_run();
    
     
    for(int i=0;i<code_template.size;i++){
      text.append(code_template.get(i));
      text.append("\n");
    } 
      
    tv_source.buffer = new Gtk.SourceBuffer.with_language(lang_man.get_language("vala"));
    tv_source.buffer.text=text.str;
    mi_save.set_sensitive(true);
  }

}

int main (string[] args)
{
    Gtk.init (ref args);
    
    GladeV gladev =  new GladeV();
    gladev.main_window.show_all ();

    Gtk.main ();
    return 0;
}
}
